mod subscriber;
use common::base::config::Config;
use std::io::Write;
use std::mem::MaybeUninit;
use std::sync::atomic::AtomicBool;
use std::sync::atomic::Ordering;
use tokio::sync::mpsc::{self, UnboundedReceiver, UnboundedSender};
use tracing::Level;

use tracing_appender::rolling::RollingFileAppender;

use self::subscriber::LoggerSubscriber;

static mut LOGGER: MaybeUninit<Logger> = MaybeUninit::uninit();
static mut LOG_SENDER: MaybeUninit<UnboundedSender<Vec<u8>>> = MaybeUninit::uninit();
static mut LOG_RECEIVER: MaybeUninit<UnboundedReceiver<Vec<u8>>> = MaybeUninit::uninit();
static IS_CLOSE: AtomicBool = AtomicBool::new(false);

struct Logger {
    level: Level,
    // file_writer: NonBlocking,
    file_appender: RollingFileAppender,
    // console_writer: NonBlocking,
    is_print_to_console: bool,
}
pub fn init(config: &Box<Config>) {
    // 获取日志等级
    let level = match config.get_str("xport", "log-level") {
        Some(level) => match level {
            "trace" => Level::TRACE,
            "debug" => Level::DEBUG,
            "info" => Level::INFO,
            "warn" => Level::WARN,
            "error" => Level::ERROR,
            _ => Level::INFO,
        },
        None => Level::INFO,
    };

    let log_dir = config
        .get_str("xport", "log-path")
        .unwrap_or_else(|| "./log");

    let writers = config.get_str_array("xport", "log-writer");

    let has_console = writers.contains(&"console");

    let file_appender = tracing_appender::rolling::daily(log_dir, "xport");

    let logger_inner = Logger {
        level,
        file_appender,
        is_print_to_console: has_console,
    };

    let subscriber = LoggerSubscriber::new(level);

    tracing::subscriber::set_global_default(subscriber).expect("初始化日志库失败！");

    let (sender, receiver) = mpsc::unbounded_channel::<Vec<u8>>();

    unsafe {
        LOG_SENDER.as_mut_ptr().write(sender);
        LOG_RECEIVER.as_mut_ptr().write(receiver);
        LOGGER.as_mut_ptr().write(logger_inner);
    }
    // 接收日志
    tokio::spawn(receive_log());
}

pub fn level() -> i32 {
    unsafe {
        match LOGGER.assume_init_ref().level {
            Level::TRACE => 0,
            Level::DEBUG => 1,
            Level::INFO => 2,
            Level::WARN => 3,
            Level::ERROR => 4,
        }
    }
}
/**
 * 发送日志
 */
pub fn append_log(log: Vec<u8>) {
    if IS_CLOSE.load(Ordering::Acquire) {
        return;
    }
    unsafe {
        let sender = LOG_SENDER.assume_init_ref();
        let result = sender.send(log);
        result.expect("写入日志失败！");
    }
}

pub async fn close() {
    let _ = IS_CLOSE.compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst);
    unsafe {
        let sender = LOG_SENDER.assume_init_ref();
        sender.closed().await;
        let receiver = LOG_RECEIVER.assume_init_mut();
        //
        receiver.close();
    }
}

async fn receive_log() {
    unsafe {
        let mut_logger = LOGGER.assume_init_mut();
        while let Some(log) = LOG_RECEIVER.assume_init_mut().recv().await {
            //
            if mut_logger.is_print_to_console {
                std::io::stdout()
                    .write(log.as_slice())
                    .expect("写入日志到控制台失败！");
            }

            mut_logger
                .file_appender
                .write(log.as_slice())
                .expect("写入日志到文件失败！");
        }
    }
}
